/*
 * FlexWire.c
 *
 */
#include "TPS929120A.h"
#include "FlexWire.h"
#include "CRC_LUT.h"
#include "system_info.h"
#include "TPS929120_APIs.h"
#include "userdefine.h"
#include "uart_drv.h"


void FlexReadWriteInternal(uint16_t dev_addr, uint16_t reg_addr, uint16_t *dataBytes, uint16_t length, uint16_t checkResponse, uint16_t checkRcvCrc)
{
    // One longest command frame length is MAX_BURST_CNT + 4
    uint16_t commandFrame[MAX_BURST_CNT + 4];
    uint16_t i = 0;
    uint16_t dataLength, sendFrameSize_u16, rcvFrameSize_u16, crcCheckSize;
    uint16_t response_needed;
    uint16_t wait_for_response;
    uint16_t check_receive_crc = (checkRcvCrc | ALWAYS_CHECK_CRC);
    uint32_t sendFrameSize_u32, rcvFrameSize_u32;

    response_needed = checkResponse;

    commandFrame[0] = SYNC; //SYNC byte
    commandFrame[1] = dev_addr;
    commandFrame[2] = reg_addr;

    // Make function to be able to distinguish TPS929120 with TPS929160 and TPS929240
    dataLength = DATA_LENGTH(length);

    if(dev_addr & BROADCAST) {
        wait_for_response = FALSE;
    }
    else {
        if((ackEnabled[dev_addr & DEVICE_ADDR__15] == FALSE) && (dev_addr & WRITE_MODE)) {
            wait_for_response = FALSE;
        }
        else {
            wait_for_response = TRUE;
        }
    }

    // Distinguish between read and write mode
    if(dev_addr & WRITE_MODE) {
        // Write mode
        sendFrameSize_u16 = 4 + dataLength;
        rcvFrameSize_u16 = 2;
        crcCheckSize = 2;
        for(i = 0; i < dataLength; i++) {
            commandFrame[i + 3] = dataBytes[i];
        }
        if(check_receive_crc == TRUE) {
            if(!(dev_addr & BROADCAST)) {
                response_needed = TRUE;
            }
        }
    }
    else {
        // Read mode
        sendFrameSize_u16 = 4;
        rcvFrameSize_u16 = 1 + dataLength;
        crcCheckSize = rcvFrameSize_u16;
    }

    // Calculate CRC of all the command frame bytes
    commandFrame[i + 3] = CRC_LUT(commandFrame, i + 3);

    for(i = 0; i < sendFrameSize_u16; i++) {
        g_sendBuffer[i] = (char) commandFrame[i];
    }
    
    // When CAN is used, the read_offset and the number of received bytes depend on the number of transmitted bytes
    if(CAN_USED == TRUE) {
        read_offset = sendFrameSize_u16;
        rcvFrameSize_u16 += sendFrameSize_u16;
    }

    sendFrameSize_u32 = (uint32_t)sendFrameSize_u16;
    rcvFrameSize_u32 = (uint32_t)rcvFrameSize_u16;
    
    // Data will be stored in ledXmtBuffer
    Transfer(sendFrameSize_u16, rcvFrameSize_u16, wait_for_response);

    rcvCrcError = FALSE;

    // If requested, check CRC
    if((response_needed == TRUE) & (check_receive_crc == TRUE)) {
        for(i = 1; i < crcCheckSize; i++) {
            commandFrame[i] = (uint16_t)g_rcvBuffer[i + read_offset - 1];
        }
        // Check if calculated CRC matched received CRC
        if(CRC_LUT(commandFrame, i) != (uint16_t)g_rcvBuffer[i + read_offset - 1]) {
            // Error occurred
            rcvCrcError = TRUE;
        }
    }

    // If checkRepsonse is not set, a minimum wait time is needed before writing next command frame, to make sure next writing command is after response finished sending
    // the minimum wait time can be calculated by the formula below
    // minimum wait time = response length * 10 *1/baudrate
}

void FlexWriteBroadcast(uint16_t reg_addr, uint16_t *dataBytes, uint16_t length)
{
    uint16_t dev_addr = 0x00;

    dev_addr = (WRITE_MODE | BROADCAST | length);

    FlexReadWriteInternal(dev_addr, reg_addr, dataBytes, length, FALSE, FALSE);
}

void FlexWrite(uint16_t dev_addr_x, uint16_t reg_addr, uint16_t *dataBytes, uint16_t length, uint16_t checkResponse)
{
    uint16_t dev_addr = 0x00;

    dev_addr = (WRITE_MODE | length | dev_addr_x);

    FlexReadWriteInternal(dev_addr, reg_addr, dataBytes, length, checkResponse, FALSE);
}

void FlexRead(uint16_t dev_addr_x, uint16_t reg_addr, uint16_t length, uint16_t checkRcvCrc)
{
    uint16_t dev_addr = 0x00;
    uint16_t data[] = {0x00};

    dev_addr = (READ_MODE | length | dev_addr_x);

    FlexReadWriteInternal(dev_addr, reg_addr, data, length, TRUE, checkRcvCrc);
}
